package jmine.tec.persist.schema.api;

import java.util.Collection;
import java.util.List;

import jmine.tec.utils.Tuple;

/**
 * Interface que deve ser implementada para dar suporte à geração de schema em dialetos de bases de dados específicas.
 * 
 * @author lundberg
 */
public interface SqlDialect {

    // Métodos para configuração do dialeto, habilitando ou desabilitar recursos

    /**
     * Força o valor do identitySupport para o valor desejado. Forçando para false em dialetos que dão suporte tanto a sequences quando
     * identity forçará a criação de sequences. Caso o dialeto não dê suporte a sequences, não possui efeito.
     * 
     * @param identitySupport identitySupport
     */
    void setForceSequenceSupport(boolean identitySupport);

    /**
     * Define se tabelas e índices devem ser designados para os tablespaces. Por padrão o tablespace é ignorado.
     * 
     * @param assignTablespaces assignTablespaces
     */
    void setAssignTablespaces(boolean assignTablespaces);

    /**
     * Obtém uma lista de statements que geram a coleção de elementos dados.
     * 
     * @param elements elements
     * @return lista de statements
     */
    List<String> renderElements(Collection<? extends SchemaElement> elements);

    /**
     * Cria uma alternative key (unique constraint) com os parâmetros dados
     * 
     * @param table nome da tabela
     * @param name nome da constraint
     * @param columns nomes das colunasStr
     * @return lista de statements
     */
    List<String> renderAlternativeKey(String table, String name, List<String> columns);

    /**
     * Cria uma check constraint com os parâmetros dados
     * 
     * @param table nome da tabela
     * @param name nome da constraint
     * @param typeCode typeCode
     * @param condition condição da constraint
     * @return lista de statements
     */
    List<String> renderCheckConstraint(String table, String name, int typeCode, String condition);

    /**
     * Cria um comentário aplicado à uma tabela
     * 
     * @param table nome da tabela comentada
     * @param comment nome do comentário
     * @return lista de statements
     */
    List<String> renderComment(String table, String comment);

    /**
     * Cria um comentário aplicado à uma coluna
     * 
     * @param table nome da tabela com a coluna comentada
     * @param column nome da coluna comentada
     * @param comment comentário
     * @return lista de statements
     */
    List<String> renderComment(String table, String column, String comment);

    /**
     * Cria uma constraint de chave estrangeira (foreign key) com os parâmetros dados.
     * 
     * @param foreignKeyName nome da constraint
     * @param referencingTable nome da tabela que referencia
     * @param referencedTable nome da tabela referenciada
     * @param referencingColumns nomes das colunas que referenciam
     * @param referencedColumns nomes das colunas referenciadas
     * @param onDeleteCascade <code>true</code> se deve cascatar, <code>false</code> caso contrario
     * @return lista de statements
     */
    List<String> renderForeignKey(String foreignKeyName, String referencingTable, String referencedTable, String[] referencingColumns,
            String[] referencedColumns, boolean onDeleteCascade);

    /**
     * Cria um índice (index) com os parâmetros dados
     * 
     * @param tablespace nome do tablespace
     * @param table tabela que deve receber o índice
     * @param name nome do índice
     * @param indexedColumns lista de tuplas contento os nomes das colunas e a ordenação que deve ser aplicada.
     * @return lista de statements
     */
    List<String> renderIndex(String tablespace, String table, String name, List<Tuple<String, IndexOrder>> indexedColumns);

    /**
     * Cria uma constraint de chave primária (primary key) com os parâmetros dados
     * 
     * @param table nome da tabela que receberá a chave primária
     * @param name nome da constraint
     * @param columns colunas que compõe a chave primária
     * @return lista de statements
     */
    List<String> renderPrimaryKey(String table, String name, List<? extends SchemaColumn> columns);

    /**
     * Cria uma sequence com os parâmetros dados.
     * 
     * @param name nome da sequence
     * @param initialValue valor inicial da sequence (pode ser nulo)
     * @return lista de statements
     */
    List<String> renderSequence(String name, Integer initialValue);

    /**
     * Cria uma tabela com os parâmetros dados.<br/>
     * Os elementos que representam as colunas sempre retornam um único elemento definindo a coluna, de acordo com a implementação de
     * SqlDialect.renderColumn()
     * 
     * @param tablespace tablespace
     * @param name nome da tabela a ser criada
     * @param columns lista de elementos do schema que representam as colunas.
     * @return lista de statements
     */
    List<String> renderTable(String tablespace, String name, Collection<? extends SchemaElement> columns);

    /**
     * Cria uma string que define a coluna de acordo com os parâmetros dados.
     * 
     * @param name nome da coluna
     * @param type elemento do schema que define o tipo da coluna
     * @param nullable indica se a coluna deve permitir ou não valores nulos.
     * @param defaultValue valor padrão da coluna
     * @return string
     */
    String renderColumn(String name, SchemaElement type, boolean nullable, String defaultValue);

    /**
     * Cria uma string que define o tipo de uma coluna, de acordo com o código definido em java.sql.Types e os parâmetros de length,
     * precision e scale que o tipo deve suportar.
     * 
     * @param code código do tipo, de acordo com java.sql.Types
     * @param length tamanho do campo, aplicável em campos como varchar
     * @param precision precisão, aplicável no caso de campos numéricos
     * @param scale escala, aplicável no caso de campos numéricos
     * @return string
     */
    String renderType(int code, int length, int precision, int scale);

    /**
     * Faz um post processamento do schema. Inicialmente para corrigir o auto_increment de acordo com o sequence value
     * 
     * @param schemaStatements List de String os statements finais
     * @param elements todos os elementos
     * @return List
     */
    List<String> postProcessSchema(List<String> schemaStatements, Collection<? extends SchemaElement> elements);

    /**
     * Renderiza o literal de uma forma apropriada para o tipo no banco.
     * 
     * @param code code
     * @param defaultValue defaultValue
     * @return literal
     */
    String renderLiteral(int code, String defaultValue);
}
